using System;
using System.Collections.Generic;
using System.Text;

// This class originially written in C by Ed Hill. I've written several variations
// of it over time. It's used here instead of any native RNGs because it 
// produces the same results over all platforms. Originally this was important
// because we processed documents on multiple platforms. Now we need
// to use it because of historical data. If, at some point in time, it is desired
// to change to a native RNG then all documents in the OR would need to be
// restamped and the entire work flow will need to run on .NET

namespace QDG.Powerball
{
    public class R250
    {
        private ulong _seed = 1;
        public ulong Seed
        {
            get {return _seed;}
            set {_seed = value; }
        }

        private int _index = -1;

        private uint[] _buffer = null;
        
        public R250()
        {
            SRand(1);
        }

        public R250(uint seed)
        {
            SRand(seed);
        }

        private void _initBuffer()
        {
            if (_buffer == null)
                _buffer = new uint[250];

            _buffer[  0]=15301; _buffer[  1]=57764; _buffer[  2]=10921; _buffer[  3]=56345; _buffer[  4]=19316;
            _buffer[  5]=43154; _buffer[  6]=54727; _buffer[  7]=49252; _buffer[  8]=32360; _buffer[  9]=49582;
            _buffer[ 10]=26124; _buffer[ 11]=25833; _buffer[ 12]=34404; _buffer[ 13]=11030; _buffer[ 14]=26232;
            _buffer[ 15]=13965; _buffer[ 16]=16051; _buffer[ 17]=63635; _buffer[ 18]=55860; _buffer[ 19]= 5184;
            _buffer[ 20]=15931; _buffer[ 21]=39782; _buffer[ 22]=16845; _buffer[ 23]=11371; _buffer[ 24]=38624;
            _buffer[ 25]=10328; _buffer[ 26]= 9139; _buffer[ 27]= 1684; _buffer[ 28]=48668; _buffer[ 29]=59388;
            _buffer[ 30]=13297; _buffer[ 31]= 1364; _buffer[ 32]=56028; _buffer[ 33]=15687; _buffer[ 34]=63279;
            _buffer[ 35]=27771; _buffer[ 36]= 5277; _buffer[ 37]=44628; _buffer[ 38]=31973; _buffer[ 39]=46977;
            _buffer[ 40]=16327; _buffer[ 41]=23408; _buffer[ 42]=36065; _buffer[ 43]=52272; _buffer[ 44]=33610;
            _buffer[ 45]=61549; _buffer[ 46]=58364; _buffer[ 47]= 3472; _buffer[ 48]=21367; _buffer[ 49]=56357;
            _buffer[ 50]=56345; _buffer[ 51]=54035; _buffer[ 52]= 7712; _buffer[ 53]=55884; _buffer[ 54]=39774;
            _buffer[ 55]=10241; _buffer[ 56]=50164; _buffer[ 57]=47995; _buffer[ 58]= 1718; _buffer[ 59]=46887;
            _buffer[ 60]=47892; _buffer[ 61]= 6010; _buffer[ 62]=29575; _buffer[ 63]=54972; _buffer[ 64]=30458;
            _buffer[ 65]=21966; _buffer[ 66]=54449; _buffer[ 67]=10387; _buffer[ 68]= 4492; _buffer[ 69]=  644;
            _buffer[ 70]=57031; _buffer[ 71]=41607; _buffer[ 72]=61820; _buffer[ 73]=54588; _buffer[ 74]=40849;
            _buffer[ 75]=54052; _buffer[ 76]=59875; _buffer[ 77]=43128; _buffer[ 78]=50370; _buffer[ 79]=44691;
            _buffer[ 80]=  286; _buffer[ 81]=12071; _buffer[ 82]= 3574; _buffer[ 83]=61384; _buffer[ 84]=15592;
            _buffer[ 85]=45677; _buffer[ 86]= 9711; _buffer[ 87]=23022; _buffer[ 88]=35256; _buffer[ 89]=45493;
            _buffer[ 90]=48913; _buffer[ 91]=  146; _buffer[ 92]= 9053; _buffer[ 93]= 5881; _buffer[ 94]=36635;
            _buffer[ 95]=43280; _buffer[ 96]=53464; _buffer[ 97]= 8529; _buffer[ 98]=34344; _buffer[ 99]=64955;
            _buffer[100]=38266; _buffer[101]=12730; _buffer[102]=  101; _buffer[103]=16208; _buffer[104]=12607;
            _buffer[105]=58921; _buffer[106]=22036; _buffer[107]= 8221; _buffer[108]=31337; _buffer[109]=11984;
            _buffer[110]=20290; _buffer[111]=26734; _buffer[112]=19552; _buffer[113]=   48; _buffer[114]=31940;
            _buffer[115]=43448; _buffer[116]=34762; _buffer[117]=53344; _buffer[118]=60664; _buffer[119]=12809;
            _buffer[120]=57318; _buffer[121]=17436; _buffer[122]=44730; _buffer[123]=19375; _buffer[124]=   30;
            _buffer[125]=17425; _buffer[126]=14117; _buffer[127]= 5416; _buffer[128]=23853; _buffer[129]=55783;
            _buffer[130]=57995; _buffer[131]=32074; _buffer[132]=26526; _buffer[133]= 2192; _buffer[134]=11447;
            _buffer[135]=   11; _buffer[136]=53446; _buffer[137]=35152; _buffer[138]=64610; _buffer[139]=64883;
            _buffer[140]=26899; _buffer[141]=25357; _buffer[142]= 7667; _buffer[143]= 3577; _buffer[144]=39414;
            _buffer[145]=51161; _buffer[146]=    4; _buffer[147]=58427; _buffer[148]=57342; _buffer[149]=58557;
            _buffer[150]=53233; _buffer[151]= 1066; _buffer[152]=29237; _buffer[153]=36808; _buffer[154]=19370;
            _buffer[155]=17493; _buffer[156]=37568; _buffer[157]=    3; _buffer[158]=61468; _buffer[159]=38876;
            _buffer[160]=17586; _buffer[161]=64937; _buffer[162]=21716; _buffer[163]=56472; _buffer[164]=58160;
            _buffer[165]=44955; _buffer[166]=55221; _buffer[167]=63880; _buffer[168]=    1; _buffer[169]=32200;
            _buffer[170]=62066; _buffer[171]=22911; _buffer[172]=24090; _buffer[173]=10438; _buffer[174]=40783;
            _buffer[175]=36364; _buffer[176]=14999; _buffer[177]= 2489; _buffer[178]=43284; _buffer[179]= 9898;
            _buffer[180]=39612; _buffer[181]= 9245; _buffer[182]=  593; _buffer[183]=34857; _buffer[184]=41054;
            _buffer[185]=30162; _buffer[186]=65497; _buffer[187]=53340; _buffer[188]=27209; _buffer[189]=45417;
            _buffer[190]=37497; _buffer[191]= 4612; _buffer[192]=58397; _buffer[193]=52910; _buffer[194]=56313;
            _buffer[195]=62716; _buffer[196]=22377; _buffer[197]=40310; _buffer[198]=15190; _buffer[199]=34471;
            _buffer[200]=64005; _buffer[201]=18090; _buffer[202]=11326; _buffer[203]=50839; _buffer[204]=62901;
            _buffer[205]=59284; _buffer[206]= 5580; _buffer[207]=15231; _buffer[208]= 9467; _buffer[209]=13161;
            _buffer[210]=58500; _buffer[211]= 7259; _buffer[212]=  317; _buffer[213]=50968; _buffer[214]= 2962;
            _buffer[215]=23006; _buffer[216]=32280; _buffer[217]= 6994; _buffer[218]=18751; _buffer[219]= 5148;
            _buffer[220]=52739; _buffer[221]=49370; _buffer[222]=51892; _buffer[223]=18552; _buffer[224]=52264;
            _buffer[225]=54031; _buffer[226]= 2804; _buffer[227]=17360; _buffer[228]= 1919; _buffer[229]=19639;
            _buffer[230]= 2323; _buffer[231]= 9448; _buffer[232]=43821; _buffer[233]=11022; _buffer[234]=45500;
            _buffer[235]=31509; _buffer[236]=49180; _buffer[237]=35598; _buffer[238]=38883; _buffer[239]=19754;
            _buffer[240]=  987; _buffer[241]=11521; _buffer[242]=55494; _buffer[243]=38056; _buffer[244]=20664;
            _buffer[245]= 2629; _buffer[246]=50986; _buffer[247]=31009; _buffer[248]=54043; _buffer[249]=59743;
        }

        public void SRand(ulong seed)
        {
            int k;

            uint mask;
            uint msb;

            Seed = seed;
            _initBuffer();

            _index = 0;

            for (int j = 0; j < 250; j++)
                _buffer[j] = _myRand();

            for (int j = 0; j < 250; j++)
            {
                if (_myRand() > 16384)
                    _buffer[j] |= 0x8000;
            }

            msb = 0x8000;          // to turn on the diagonal bit
            mask = 0xffff;            // to turn off the leftmost bits

            for (int j = 0; j < 16; j++)
            {
                k = 11 * j + 3;
                _buffer[k] &= mask;
                _buffer[k] |= msb;
                mask >>= 1;
                msb >>= 1;
            }
        }

        private uint _myRand()
        {
            Seed = Seed * 0x015a4e35L + 1; 

            return (uint)((Seed >> 16) & 0x7fff);
        }

        /// <summary>
        /// returns a random unsigned integer uniformly distributed in the interval
        /// 0 *lt;= k &lt; 65536
        /// </summary>
        /// <returns>uint</returns>
        public uint Rand()
        {
            uint newRand;

            int j;

            if (_index >= 147)
                j = _index - 147;
            else
                j = _index + 103;

             newRand = _buffer[_index] ^ _buffer[j];
             _buffer[_index] = newRand;

            if (_index >= 249)
                _index = 0;
            else
                _index++;

            return newRand;
        }

        public uint IMax
        {
            get {return 65536;}
        }

        /// <summary>
        /// returns a uint k uniformly distributed in the interval
        /// 0 &lt;= k &lt; n
        /// </summary>
        /// <param name="n">upper limit of range</param>
        /// <returns>0 &lt;= k &lt; n</returns>
        public uint RandN(uint n)
        {
            if (n <= 1)
                throw new ArgumentOutOfRangeException("n", "Range must be greater than 1");

            uint newRand;
            uint limit;

            int j;

            limit = (65535U/ n) * n;

            do
            {
                newRand = Rand();
                if (_index >= 147)
                    j = _index - 147;
                else
                    j = _index + 103;

                newRand = _buffer[_index] ^ _buffer[j];
                _buffer[_index] = newRand;

                if (_index >= 249)
                    _index = 0;
                else
                    _index++;
            } while (newRand >= limit);

            return newRand % n;
        }

        /// <summary>
        /// returns a random double z uniformly distributed in the interval
        /// 0 &lt;= z &lt; 1
        /// </summary>
        /// <returns>0 &lt;= z &lt; 1</returns>
        public double Flat()
        {
            int j;
            uint newRand;

            if (_index >= 147)
                j = _index - 147;
            else
                j = _index + 103;

            newRand = _buffer[_index] ^= _buffer[j];

            if (_index >= 249)
                _index = 0;
            else
                _index++;

            return (newRand / 65536.0);
        }
    }
}
